#include "msg.h"
#include "codec_engine.h"
#include <string.h>
#include <arpa/inet.h>

CCSHead::CCSHead()
{
    m_nTotalLength = 0;
    m_nVer = 0;
//#ifdef _CSHEAD_HAS_DLGID_
    m_nDialogID = -1;
//#endif
    m_iSeq = 0;
    m_iUin = 0;
    m_byBodyFlag = 0;
    m_byOptLength = 0;
    memset((void *)m_abyOption, 0, sizeof(m_abyOption));
}

CCSHead::~CCSHead()
{
}

int CCSHead::Encode(char *pcOutBuffer, short sOutLength)
{
    CEncodeStream stEncodeStream;

    int iRet = stEncodeStream.Initialize((unsigned char*)pcOutBuffer, (int)sOutLength);

    if(iRet)
    {
//         TRACEMSG(LOG_LEVEL_WARN, "iRet = stEncodeStream.Initialize(); RESULT: iRet = %d, ERRORNO = 0x%08x\n", 
//             iRet, stEncodeStream.GetErrorNO());

        return -1;
    }

    stEncodeStream << m_nTotalLength << m_nVer << m_iSeq 
//#ifdef _CSHEAD_HAS_DLGID_
        << m_nDialogID
//#endif
        << m_iUin << m_byBodyFlag;

    if( m_byOptLength > MSG_CSHEAD_MAXOPT_LEN )
    {
        m_byOptLength = MSG_CSHEAD_MAXOPT_LEN;
    }

    stEncodeStream << m_byOptLength;

    if( m_byOptLength > 0 )
    {
        stEncodeStream.EncodeMem((char*)m_abyOption, m_byOptLength);
    }

    return stEncodeStream.GetCurOffset();
}

int CCSHead::Decode(const char *pcInBuffer, const short shInLength)
{
    CDecodeStream stDecodeStream;

    int iRet = stDecodeStream.Initialize((unsigned char*)pcInBuffer, (int)shInLength);

    if(iRet)
    {
//         TRACEMSG(LOG_LEVEL_WARN, "iRet = stDecodeStream.Initialize(); RESULT: iRet = %d, ERRORNO = 0x%08x\n", 
//             iRet, stDecodeStream.GetErrorNO());

        return -1;
    }

    stDecodeStream >> m_nTotalLength >> m_nVer >> m_iSeq 
//#ifdef _CSHEAD_HAS_DLGID_
        >> m_nDialogID
//#endif
        >> m_iUin >> m_byBodyFlag >> m_byOptLength;

    if( m_byOptLength > MSG_CSHEAD_MAXOPT_LEN )
    {
        m_byOptLength = MSG_CSHEAD_MAXOPT_LEN;
    }

    if( m_byOptLength > 0 )
    {
        stDecodeStream.DecodeMem((char*)m_abyOption, m_byOptLength);
    }

    if(stDecodeStream.GetLeftLength() < 0)
    {		
//         TRACEMSG(LOG_LEVEL_WARN, "stDecodeStream.GetLeftLength():%d < 0\n", 
//             stDecodeStream.GetLeftLength());

        return -2;
    }

    return stDecodeStream.GetCurOffset();
}

void CCSHead::BackfillTotalLength( char *pcOutBuffer, short Length ) const
{
    *(short*)pcOutBuffer = htons(Length);
}

void CCSHead::Print()
{
//     TRACEMSG(LOG_LEVEL_DETAIL, 
//         "CSHead|%d|%d|%d|%d|%u|%d|%d|\n", m_nTotalLength, m_nVer, m_iSeq, 
//         m_nDialogID, m_iUin, m_byBodyFlag, m_byOptLength);
}

Function_CreateProtocolBody CMsg::m_pfCreateProtocolBody = NULL;

CMsgHead::CMsgHead()
{
    m_shMsgID = -1;
    m_shMsgType = -1;
    m_iMsgSeq = -1;
    m_bSrcFE = (unsigned char)0xff;
    m_bDstFE = (unsigned char)0xff;
    m_shSrcID = -1;
    m_shDstID = -1;
}

CMsgHead::~CMsgHead()
{

}

int CMsgHead::Encode(char *pcOutBuffer, short sOutLength)
{
    CEncodeStream stEncodeStream;

    int iRet = stEncodeStream.Initialize((unsigned char*)pcOutBuffer, (int)sOutLength);

    if(iRet)
    {
//         TRACEMSG(LOG_LEVEL_WARN, "iRet = stEncodeStream.Initialize(); RESULT: iRet = %d, ERRORNO = 0x%08x\n", 
//             iRet, stEncodeStream.GetErrorNO());

        return -1;
    }

    stEncodeStream << m_shMsgID << m_shMsgType << m_iMsgSeq << m_bSrcFE
        << m_bDstFE << m_shSrcID << m_shDstID;

    return stEncodeStream.GetCurOffset();
}

int CMsgHead::Decode(const char *pcInBuffer, const short shInLength)
{
    CDecodeStream stDecodeStream;

    int iRet = stDecodeStream.Initialize((unsigned char*)pcInBuffer, (int)shInLength);

    if(iRet)
    {
//         TRACEMSG(LOG_LEVEL_WARN, "iRet = stDecodeStream.Initialize(); RESULT: iRet = %d, ERRORNO = 0x%08x\n", 
//             iRet, stDecodeStream.GetErrorNO());

        return -1;
    }

    stDecodeStream >> m_shMsgID >> m_shMsgType >> m_iMsgSeq >> m_bSrcFE
        >> m_bDstFE >> m_shSrcID >> m_shDstID;

    if(stDecodeStream.GetLeftLength() < 0)
    {		
//         TRACEMSG(LOG_LEVEL_WARN, "stDecodeStream.GetLeftLength():%d < 0\n", 
//             stDecodeStream.GetLeftLength());

        return -2;
    }

    return stDecodeStream.GetCurOffset();
}

void CMsgHead::Print()
{
//     TRACEMSG_EX(LOG_LEVEL_DETAIL, m_shMsgID, 
//         "MsgHead|%d|%d|%d|%d|%d|%d|%d|\n", 
//         m_shMsgID, m_shMsgType, m_iMsgSeq, m_bSrcFE, 
//         m_bDstFE, m_shSrcID, m_shDstID);
}

CMsg::CMsg()
{
    m_pMsgPara = NULL;
}

CMsg::~CMsg()
{
    if(m_pMsgPara)
    {
        delete m_pMsgPara;
        m_pMsgPara = NULL;
    }
}

int CMsg::CreatePara()
{
    if(m_pMsgPara)
    {
        delete m_pMsgPara;
        m_pMsgPara = NULL;
    }

    if(!m_pfCreateProtocolBody)
    {
/*        ERRORLOG("m_pfCreateProtocolBody == NULL\n");*/

        return -1;
    }

    m_pMsgPara = (*CMsg::m_pfCreateProtocolBody)(m_stHead.m_shMsgID, 
        m_stHead.m_shMsgType);

    if(!m_pMsgPara)
    {
//         TRACEMSG(LOG_LEVEL_WARN, "m_pMsgPara == NULL, MsgID = %d, MsgType = %d\n", 
//             m_stHead.m_shMsgID, m_stHead.m_shMsgType);

        return -2;
    }

    return 0;
}

int CMsg::Encode(char *pcOutBuffer, short sOutLength)
{
    if(!pcOutBuffer)
    {
/*        TRACEMSG(LOG_LEVEL_WARN, "pcOutBuffer == NULL\n");*/

        return -1;
    }

    char *pcTemp = pcOutBuffer;
    int EncodeLength = 0;

    int iRet = m_stHead.Encode(pcTemp, sOutLength - EncodeLength);

    if(iRet < 0)
    {
//         TRACEMSG(LOG_LEVEL_WARN, 
//             "iRet = m_stHead.Encode(pcTemp, shMsgHeadLength); RESULT: iRet = %d\n", iRet);

        return -2;
    }

    EncodeLength += iRet;
    pcTemp += iRet;

    if(!m_pMsgPara)
    {
/*        TRACEMSG(LOG_LEVEL_WARN, "m_pMsgPara == NULL\n");*/

        return 0;
    }

    iRet = m_pMsgPara->Encode(pcTemp, sOutLength - EncodeLength);

    if(iRet < 0)
    {
//         TRACEMSG_EX(LOG_LEVEL_WARN, m_stHead.m_shMsgID, 
//             "iRet = m_pMsgPara->Encode(pcTemp, shMsgParaLength); RESULT: iRet = %d\n", iRet);

        return -3;
    }

    EncodeLength += iRet;
    pcTemp += iRet;

    return EncodeLength;
}

int CMsg::Decode(const char *pcInBuffer, const short shInLength)
{
    char *pcTemp;
    short sLeftLength;

    if(!pcInBuffer || shInLength <= 0)
    {
/*        TRACEMSG(LOG_LEVEL_WARN, "pcInBuffer == NULL\n");*/

        return -1;
    }

    pcTemp = (char *)pcInBuffer;
    sLeftLength = shInLength;
    int DecodeLength = 0;

    int iRet = m_stHead.Decode((const char *)pcTemp, (const short)sLeftLength);

    if(iRet < 0)
    {
//         TRACEMSG(LOG_LEVEL_WARN, 
//             "iRet = m_stHead.Decode(pcTemp, sLeftLength); RESULT: iRet = %d\n", iRet);

        return -2;
    }

    DecodeLength += iRet;
    sLeftLength -= MSG_MSGHEAD_SIZE;
    pcTemp += MSG_MSGHEAD_SIZE;

    if(m_pMsgPara)
    {
        delete m_pMsgPara;
        m_pMsgPara = NULL;
    }

    if(sLeftLength <= 0)
    {
        if(sLeftLength == 0)
        {
            return 0;
        }
        else
        {
//             TRACEMSG(LOG_LEVEL_WARN, 
//                 "sLeftLength:%d < 0;\n", sLeftLength);

            return -3;
        }
    }

    iRet = CreatePara();

    if(iRet)
    {
//         TRACEMSG(LOG_LEVEL_WARN, 
//             "iRet = CreatePara(); RESULT: iRet = %d\n", iRet);

        return -4;
    }

    iRet = m_pMsgPara->Decode((const char *)pcTemp, (const short)sLeftLength);

    if(iRet < 0)
    {
//         TRACEMSG_EX(LOG_LEVEL_WARN, m_stHead.m_shMsgID, 
//             "iRet = m_pMsgPara->Decode(); RESULT: iRet = %d\n", iRet);

        return -5;
    }

    DecodeLength += iRet;
    return DecodeLength;
}

void CMsg::Print()
{
    m_stHead.Print();

    if(m_pMsgPara)
    {
        m_pMsgPara->Print();
    }
}

void CMsg::RegisterProtocolCreator(Function_CreateProtocolBody pfCreateProtocolBody)
{
    CMsg::m_pfCreateProtocolBody = pfCreateProtocolBody;
}
